﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows.Media.Animation;
using UBCon.Ribbon.Interfaces;
using UBCon.Ribbon.Models;

namespace UBCon.Ribbon
{
    [TemplatePart(Name = PART_PopupName, Type=typeof(Popup))]
    [TemplatePart(Name = PART_RibbonDropDownButtonThumbName, Type = typeof(Thumb))]
    [TemplatePart(Name = PART_RibbonDropDownButtonResizeName, Type = typeof(FrameworkElement))]
    [StyleTypedProperty(Property="ItemContainerStyle", StyleTargetType = typeof(RibbonComboBoxItem))]
    public class RibbonComboBox : ComboBox, IRibbonControl
    {
        #region Constructors

        /// <summary>
        /// Default costructor.
        /// </summary>
        public RibbonComboBox()
        {

        }

        /// <summary>
        /// Deafaul static constructor.
        /// </summary>
        static RibbonComboBox() 
        {
            Type ownerType = typeof(RibbonComboBox);
            
            RibbonComboBox.DefaultStyleKeyProperty.OverrideMetadata
                (
                    ownerType,
                    new FrameworkPropertyMetadata(ownerType)
                );

            RibbonComboBox.IsDropDownOpenProperty.OverrideMetadata
                (
                    ownerType,
                    new FrameworkPropertyMetadata(
                        false,
                        new PropertyChangedCallback(OnIsDropDownOpenPropertyChanged))
                );

        }

        #endregion

        #region Properties

        private bool isFilterComboBox = false;

        internal bool IsFilterComboBox 
        {
            get 
            {
                return isFilterComboBox;
            }
            set 
            {
                isFilterComboBox = value;
            }
        }
        public static readonly DependencyProperty UserCanResizeDropDownProperty = DropDownButtonBase.UserCanResizeDropDownProperty.AddOwner(typeof(RibbonComboBox));

        /// <summary>
        /// Indicates whether drop down content can be animated or not
        /// </summary>
        protected internal bool ContentCanBeAnimated
        {
            get
            {
                return this._dropDownPopup != null
                        && this._dropDownPopup.Child != null
                            && this._dropDownPopup.Child is FrameworkElement
                            && this._dropDownPopup.Child.RenderTransform is TranslateTransform;
            }
        }

        /// <summary>
        /// Gets drop down's content
        /// </summary>
        protected FrameworkElement DropDownContent
        {
            get { return this._dropDownPopup.Child as FrameworkElement; }
        }

        
        #endregion

        #region Depenency Property Changed Callbacks

        /// <summary>
        /// Invoked when AnimationsEnabled property changes.
        /// </summary>
        /// <param name="sender">Type: System.Windows.DependencyObject. The DependencyObject on which the property has changed value.</param>
        /// <param name="e">Type: System.Windows.DependencyPropertyChangedEventArgs. Event data that is issued by any event that tracks changes to the effective value of this property.</param>
        private static void OnIsDropDownOpenPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) 
        {
            bool newValue = (bool)e.NewValue;
            
            RibbonComboBox combo = sender as RibbonComboBox;
            
            if (newValue)
            {
                combo.TryAnimateDropDownContent();
            }
            else 
            {
                //combo.RestoreContent();
            }
        }

        private bool IsItemFilterModel 
        {
            get 
            {
                return SelectedItem != null && SelectedItem is FilterItemModel;
            }
        }
        #endregion

        #region Overrides

        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
        {
            base.OnSelectionChanged(e);
            if (IsItemFilterModel)
            {
                FilterItemModel model = SelectedItem as FilterItemModel;
                model.IsSelected = true;
                if (this.TemplatedParent is RibbonGallerySet) 
                {
                    RibbonGallerySet gallerySet = this.TemplatedParent as RibbonGallerySet;
                    gallerySet.OnFilterItemSelected(model);
                }
            }
        }

        protected override DependencyObject GetContainerForItemOverride()
        {
            return new RibbonComboBoxItem() { ParentComboBox = this };
        }

        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return item is RibbonComboBoxItem;
        }
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            this._dropDownPopup = this.GetTemplateChild(PART_PopupName) as Popup;

            this._thumb = this.GetTemplateChild(PART_RibbonDropDownButtonThumbName) as Thumb;

            if (ContentCanBeAnimated)
            {
                RestoreContent();
            }

            if (this._thumb != null)
            {
                this._thumb.DragDelta += _thumb_DragDelta;
                this._thumb.DragCompleted += _thumb_DragCompleted;
            }
            this.resizeControl = this.GetTemplateChild(PART_RibbonDropDownButtonResizeName) as FrameworkElement;
        }
        
        #endregion

        #region Private Utilities

        private void _thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            double height = this.resizeControl.ActualHeight;
            height += e.VerticalChange;
            if (height >= this.resizeControl.MinHeight + 2 && height <= this.MaxDropDownHeight)
            {
                this.resizeControl.SetValue(FrameworkElement.HeightProperty, height);
            }
        }

        void _thumb_DragCompleted(object sender, DragCompletedEventArgs e)
        {
            //  Mouse.Capture(this, CaptureMode.SubTree);
            //System.Console.WriteLine(Mouse.Captured);
            thumbCaptured = true;
        }

        protected internal void RestoreContent()
        {
            if (this.HideContentStoryboard == null)
            {
                this.BuildHideContentStoryboard();
            }
            this.HideContentStoryboard.Begin();
        }

        protected internal void TryAnimateDropDownContent()
        {
            if (this.ContentCanBeAnimated)
            {
                if (this.ShowContentStoryboard == null)
                {
                    this.BuildShowContentAnimation();
                }
                if (this.resizeControl != null)
                {
                    this.resizeControl.Height = this.initialDropDownHeight;
                }
                this.ShowContentStoryboard.Begin();
            }
        }

        private void BuildShowContentAnimation()
        {
            DoubleAnimation da = new DoubleAnimation() 
            { 
                EasingFunction = new ExponentialEase() 
                { 
                    EasingMode = EasingMode.EaseOut, Exponent = 5 
                } 
            };
            TranslateTransform transform = this._dropDownPopup.Child.RenderTransform as TranslateTransform;
            Storyboard.SetTargetProperty(da, new PropertyPath(propertyPath));
            Storyboard.SetTarget(da, this.DropDownContent);
            this.ShowContentStoryboard = new Storyboard();
            da.From = -5D;
            da.To = 0.0D;
            da.Duration = new Duration(TimeSpan.FromSeconds(0.20));
            //this.ShowContentStoryboard.Completed += ShowContentStoryboard_Completed;
            this.ShowContentStoryboard.Children.Add(da);
        }

        private void ShowContentStoryboard_Completed(object sender, EventArgs e)
        {
            this.ShowContentStoryboard.Completed -= ShowContentStoryboard_Completed;
            if (this.resizeControl != null)
            {
                this.initialDropDownHeight = this.resizeControl.ActualHeight;
            }
        }

        private void BuildHideContentStoryboard()
        {
            this.HideContentStoryboard = new Storyboard();
            DoubleAnimation da = new DoubleAnimation();
            Storyboard.SetTargetProperty(da, new PropertyPath(propertyPath));
            Storyboard.SetTarget(da, this.DropDownContent);
            da.From = 0.0D;
            da.To = -5;
            da.Duration = new Duration(TimeSpan.FromSeconds(0.02));
            this.HideContentStoryboard.Children.Add(da);
        }

        #endregion

        #region Event Handlers
        #endregion

        #region Fields

        private double initialDropDownHeight = double.NaN;
        internal bool thumbCaptured = false;

        protected internal Popup _dropDownPopup;

        protected internal Thumb _thumb;

        protected internal FrameworkElement resizeControl;

        //property path to animate
        private const string propertyPath = "(UIElement.RenderTransform).(TranslateTransform.Y)";
        private const string PART_PopupName = "PART_Popup";
        private const string PART_RibbonDropDownButtonThumbName = "PART_RibbonDropDownButtonThumb";
        private const string PART_RibbonDropDownButtonResizeName = "PART_RibbonDropDownButtonResize";

        //content animation
        private Storyboard ShowContentStoryboard, HideContentStoryboard;

        #endregion

        #region Events
        #endregion

        #region IRibbonControl Implementation

        /// <summary>
        /// Gets a boolean value indicating whether the contol's size is large
        /// </summary>
        public bool IsLarge
        {
            get { return false; }
        }

        #endregion
    }
}
